import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;

import javax.swing.*;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import static javax.media.opengl.GL.*;  // GL constants
import static javax.media.opengl.GL2.*; // GL2 constants
import com.jogamp.opengl.util.FPSAnimator;

/**
 * The class that implements the JOGL 2D API.
 * 
 *       Created 27/apr/2013.
 * @author Pietro Bolognesi matr. 227383
 * @author Giorgio Lazzaretti matr. 220232
 *  
 */
public class GlWorld2D extends JPanel implements GLEventListener {
	private static final int REFRESH_FPS = 60;    // Display refresh frames per second
	private GLU glu;             // For the GL Utility
	/**
	 * Animator.
	 */
	final FPSAnimator animator;  // Used to drive display() 
	/**
	 * The list that contains the objects generated
	 */
	private static ArrayList<GenericGlObject> objectList;

	// Constructor
	/**
	 *Consrtuctor of the 2D world
	 *
	 */
	public GlWorld2D() {
		GLCanvas canvas = new GLCanvas();
		this.setLayout(new BorderLayout());
		this.add(canvas, BorderLayout.CENTER);
		canvas.addGLEventListener(this);

		// Run the animation loop using the fixed-rate Frame-per-second animator,
		// which calls back display() at this fixed-rate (FPS).
		this.animator = new FPSAnimator(canvas, REFRESH_FPS, true);
	}


	
	/**
	 * Main 2D program.
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		final int WINDOW_WIDTH = 640;
		final int WINDOW_HEIGHT = 480;
		final String WINDOW_TITLE = "!!2D World!!";
		objectList = new ArrayList <GenericGlObject> ();
		/*
		//Esempio di come riempire la lista
		GlTriangle tr = new GlTriangle();
		tr.SetPos(-1.5f, 0.0f, 0.0f);
		AddGenericObject(tr);
		
		GlSquare sq = new GlSquare();
		sq.SetPos(1.5f, 0.0f, 0.0f);
		AddGenericObject(sq);
		*/
		JFrame frame = new JFrame();
		final GlWorld2D joglMain = new GlWorld2D();
		frame.setContentPane(joglMain);
		frame.addWindowListener(new WindowAdapter() {
			@Override 
			public void windowClosing(WindowEvent e) {
				// Use a dedicate thread to run the stop() to ensure that the
				// animator stops before program exits.
				new Thread() {
					@Override 
					public void run() {
						joglMain.animator.stop(); // stop the animator loop
						System.exit(0);
					}
				}.start();
			}
		});
		frame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
		frame.setTitle(WINDOW_TITLE);
		frame.setVisible(true);
		joglMain.animator.start(); // start the animation loop
	}

	
	/**
	 * Ini<ializza la finestra da mettere nel main
	 *
	 */
	public void InitializeWorld2D(){
		final int WINDOW_WIDTH = 640;
		final int WINDOW_HEIGHT = 480;
		final String WINDOW_TITLE = "!!2D World!!";
		objectList = new ArrayList <GenericGlObject> ();
		JFrame frame = new JFrame();
		final GlWorld2D joglMain = new GlWorld2D();
		frame.setContentPane(joglMain);
		frame.addWindowListener(new WindowAdapter() {
			@Override 
			public void windowClosing(WindowEvent e) {
				// Use a dedicate thread to run the stop() to ensure that the
				// animator stops before program exits.
				new Thread() {
					@Override 
					public void run() {
						joglMain.animator.stop(); // stop the animator loop
						System.exit(0);
					}
				}.start();
			}
		});
		frame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
		frame.setTitle(WINDOW_TITLE);
		frame.setVisible(true);
		joglMain.animator.start(); // start the animation loop
	}
	// ------ Implement methods declared in GLEventListener ------

	/**
	 * Called back immediately after the OpenGL context is initialized. Can be used 
	 * to perform one-time initialization. Run only once.
	 */
	@Override
	public void init(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2(); // Get the OpenGL graphics context
		this.glu = new GLU(); // GL Utilities
		gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  // Set background (clear) color
		gl.glClearDepth(1.0f); // Set clear depth value to farthest
		gl.glEnable(GL_DEPTH_TEST); // Enables depth testing
		gl.glDepthFunc(GL_LEQUAL); // The type of depth test to do
		// Do the best perspective correction
		gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
		// Enable smooth shading, which blends colors nicely, and smoothes out lighting.
		gl.glShadeModel(GL_SMOOTH);

	
	}

	/**
	 * Call-back handler for window re-size event. Also called when the drawable is 
	 * first set to visible.
	 */
	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		GL2 gl = drawable.getGL().getGL2(); // Get the OpenGL graphics context

		if (height == 0) {
			height = 1; // prevent divide by zero
		}
		float aspect = (float)width / height;

		// Set the viewport (display area) to cover the entire window
		gl.glViewport(0, 0, width, height);

		// Setup perspective projection, with aspect ratio matches viewport
		gl.glMatrixMode(GL_PROJECTION); // Choose projection matrix
		gl.glLoadIdentity(); // Reset projection matrix
		this.glu.gluPerspective(45.0, aspect, 0.1, 100.0); // fovy, aspect, zNear, zFar

		// Enable the model-view transform
		gl.glMatrixMode(GL_MODELVIEW);
		gl.glLoadIdentity(); // reset
	}

	/**
	 * Called back by the animator to perform rendering.
	 */
	@Override
	public void display(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2(); // Get the OpenGL graphics context
		gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers
		gl.glLoadIdentity();  // reset the model-view matrix

		//Ciclo di renderizzazione degli oggetti
		for(int i = 0; i < objectList.size(); i++){
			gl.glPushMatrix();
			float x = objectList.get(i).GetXPos();
			float y = objectList.get(i).GetYPos();
			gl.glTranslatef(x, y, -6.0f); 
			objectList.get(i).SetGL(gl);
			objectList.get(i).DrawObject();
			gl.glPopMatrix();

		}


	}

	

	@Override
	public void dispose(GLAutoDrawable drawable) {
		// TODO Auto-generated method stub

	}


	/**
	 * return an object from the list of the object
	 *
	 * @param Id id of the object to get
	 * @return GenericGlObject
	 */
	public GenericGlObject GetGlObjectById(int Id){
		int i;
		boolean Trovato = false;
		for (i = 0; i < objectList.size(); i++){
			if(objectList.get(i).GetId() == Id){
				Trovato = true;
				break;
			}
		}
		if (Trovato)
			return objectList.get(i);

		return null;
	}

	/**
	 * Remove an object from the list of the objects.
	 *
	 * @param Id id of the object to remove
	 */
	public void RemoveGlObjectById(int Id){
		objectList.remove(GetGlObjectById(Id));
	}

	/**
	 * Clear all the objects from the list of the objects.
	 *
	 */
	public void ClearObjList(){
		objectList.clear();
	}

	/**
	 * Add an object to the list of the object, it can be any type of object.
	 *
	 * @param obj the object to add.
	 */
	public void AddGenericObject(GenericGlObject obj){
		objectList.add(obj);
	}
}
